using System.Timers;
using TicTacToe.Core;
using Newtonsoft.Json.Linq;

namespace _3DTicTacToe.Net
{
    sealed class Introduction
    {
        private readonly Connection _connection;
        private readonly Player _playerInfo;

        private readonly Timer _completeCheckTimer;
        private readonly Timer _sendTimer;

        private Player _remotePlayer;
        private bool _infoReceived;
        private bool _ackReceived;

        public delegate void IntroductionComplete(Player remotePlayer);
        public event IntroductionComplete OnIntroductonComplete;

        public void Introduce()
        {
            Log.Add("Introduction started", "Introduction");

            _connection.OnPlayerInfoAckReceived += AckReceived;
            _connection.OnPlayerInfoReceived += InfoReceived;

            _completeCheckTimer.Elapsed += IsComplete;
            _completeCheckTimer.Start();

            _sendTimer.Elapsed += SendPlayerInfo;
            _sendTimer.Start();
        }

        public void Abort()
        {
            _completeCheckTimer.Stop();
            _sendTimer.Stop();
            Shutdown();
        }

        void SendPlayerInfo(object sender, ElapsedEventArgs e)
        {
            var msg = new NetMessage("player-info", _playerInfo.Serialize());
            _connection.SendMessage(msg);
        }

        void SendAck()
        {
            var msg = new NetMessage("ack-player-info", new JObject());
            _connection.SendMessage(msg);
        }

        void IsComplete(object sender, ElapsedEventArgs e)
        {
            if (_infoReceived && _ackReceived)
            {
                _completeCheckTimer.Stop();
                Log.Add("Introduction complete", "Introduction");

                OnIntroductonComplete(_remotePlayer);

                Shutdown();
            }
        }

        void Shutdown()
        {
            _completeCheckTimer.Stop();
            _sendTimer.Stop();
            _completeCheckTimer.Dispose();
            _sendTimer.Dispose();

            _connection.OnPlayerInfoReceived -= InfoReceived;
            _connection.OnPlayerInfoAckReceived -= AckReceived;
        }

        void InfoReceived(NetMessage message)
        {
            _remotePlayer = Serialization.DeserializePlayer(message.Content);
            SendAck();
            _infoReceived = true;

            Log.Add("Player info received", "Introduction");
        }

        void AckReceived()
        {
            _sendTimer.Stop();
            _ackReceived = true;

            Log.Add("Ack received", "Introduction");
        }

        public Introduction(Connection connection, Player playerInfo)
        {
            _connection = connection;
            _playerInfo = playerInfo;

            _completeCheckTimer = new Timer(300);
            _sendTimer = new Timer(300);
        }
    }
}